#include "StaticSound.h"
#include <string>
#include <iostream>
#include "Sound.h"
#include "SoundManager.h"

#pragma once

namespace UniverseSound
{
	StaticSound::StaticSound(const Ogre::String &name) : SoundClip(name), mAudioFile(0), mVorbisInfo(0), mVorbisComment(0), mCurrentPlayPosition(0), mAudioName(0), mBuffer(0)
	{
		mStream = false;
	}

	StaticSound::~StaticSound()
	{
		_release();
		mAudioFile = 0;
		mVorbisInfo = 0;
		mVorbisComment = 0;
		mBufferData.clear();
	}

	void StaticSound::open(const Ogre::String &fileName, ALuint &buffer)
	{
		mBuffer = buffer;
		mAudioName = fileName;
		mFileOpened = true;
	}

	void StaticSound::open(Ogre::DataStreamPtr &fileStream)
	{
		int result;

		mAudioStream = fileStream;

		mAudioName = mAudioStream->getName();

		if (!ov_seekable(mOggStream))
		{
			mOggCallbacks.seek_func = NULL;
			mOggCallbacks.tell_func = NULL;
			mSeekable = false;
		}

		if ((result = ov_open_callbacks(&mAudioStream, mOggStream, NULL, 0, mOggCallbacks)) < 0)
		{
			Ogre::LogManager::getSingleton().logMessage("UniverseSound::StaticSound::open(...) - Unable to open the OGG stream", Ogre::LML_NORMAL);
			return;
		}

		mVorbisInfo = ov_info(mOggStream, -1);
		mVorbisComment = ov_comment(mOggStream, -1);

		mPlayTime = ov_time_total(mOggStream, -1);

		if (!_queryBufferInfo())
		{
			Ogre::LogManager::getSingleton().logMessage("UniverseSound::StaticSound::open(...) - The sound format is not supported!", Ogre::LML_NORMAL);
			return;
		}

		alGenBuffers(1, &mBuffer);

		char *data;
		int sizeRead = 0;
		int bitStream;

		data = new char[mBufferSize];
		do 
		{
			sizeRead = ov_read(mOggStream, data, static_cast<int>(mBufferSize), 0, 2, 1, &bitStream);
			mBufferData.insert(mBufferData.end(), data, data + sizeRead);
		}

		while (sizeRead > 0);
		delete [] data;

		if (SoundManager::getSingleton().isXRAMSupported())
		{
			SoundManager::getSingleton().setXRAMBuffer(1, &mBuffer);
		}

		alGetError();
		alBufferData(mBuffer, mFormat, &mBufferData[0], static_cast<ALsizei>(mBufferData.size()), mVorbisInfo->rate);
		if (alGetError != AL_NO_ERROR)
		{
			Ogre::LogManager::getSingleton().logMessage("UniverseSound::StaticSound::open(...) - Unable to load audio data into buffer", Ogre::LML_NORMAL);
		}

		SoundManager::getSingleton().registerSharedBuffer(mAudioName, mBuffer);

		mFileOpened = true;
	}

	void StaticSound::_release()
	{
		ALuint src = AL_NONE;
		setSource(src);
		SoundManager::getSingleton().releaseSharedBuffer(mAudioName, mBuffer);
		if (!mAudioStream.isNull())
			ov_clear(mOggStream);
		mPlayPosChanged = false;
		mPlayPos = 0.f;
	}

	void StaticSound::_prebuffer()
	{
		if (mSource = AL_NONE)
			return;

		alSourcei(mSource, AL_BUFFER, mBuffer);
	}

	bool StaticSound::_queryBufferInfo()
	{
		if (!mVorbisInfo)
		{
			Ogre::LogManager::getSingleton().logMessage("UniverseSound::StaticSound::_queryBufferInfo() - No vorbis information available!", Ogre::LML_NORMAL);
			return false;
		}

		switch (mVorbisInfo->channels)
		{
		case 1:
			{
				mFormat = AL_FORMAT_MONO16;
				mBufferSize = mVorbisInfo->rate >> 1;
				mBufferSize -= (mBufferSize % 2);
			}
			break;
		case 2:
			{
				mFormat = AL_FORMAT_STEREO16;
				mBufferSize = mVorbisInfo->rate;
				mBufferSize -= (mBufferSize % 4);
			}
			break;
		case 4:
			{
				mFormat = alGetEnumValue("AL_FORMAT_QUAD16");
				if (!mFormat)
					return false;
				mBufferSize = mVorbisInfo->rate * 2;
				mBufferSize -= (mBufferSize % 8);
			}
			break;
		case 6:
			{
				mFormat = alGetEnumValue("AL_FORMAT_51CHN16");
				if (!mFormat)
					return false;
				mBufferSize = mVorbisInfo->rate * 3;
				mBufferSize -= (mBufferSize % 12);
			}
			break;
		case 7:
			{
				mFormat = alGetEnumValue("AL_FORMAT_61CHN16");
				if (!mFormat)
					return false;
				mBufferSize = mVorbisInfo->rate * 4;
				mBufferSize = (mBufferSize % 16);
			}
			break;
		case 8:
			{
				mFormat = alGetEnumValue("AL_FORMAT_71CHN16");
				if (!mFormat)
					return false;
				mBufferSize = mVorbisInfo->rate * 5;
				mBufferSize = (mBufferSize & 20);
			}
			break;
		default:
			Ogre::LogManager::getSingleton().logMessage("UniverseSound - Unable to determine buffer format", Ogre::LML_NORMAL);
			Ogre::LogManager::getSingleton().logMessage("UniverseSound - Trying to set buffer format to default mono ...", Ogre::LML_NORMAL);
			mFormat = AL_FORMAT_MONO16;
			mBufferSize = mVorbisInfo->rate >> 1;
			mBufferSize -= (mBufferSize % 2);
			break;
		}
		return true;
	}

	void StaticSound::setSource(ALuint &source)
	{
		if (source != AL_NONE)
		{
			mSource = source;
			_prebuffer();
			_initSource();
		}
		else
		{
			alSourceStop(mSource);
			alSourcei(mSource, AL_BUFFER, 0);
			mSource = source;
		}
	}

	void StaticSound::pause()
	{
		boost::recursive_mutex::scoped_lock l(mMutex);

		if (!mFileOpened)
		{
			if (!mPauseDelayed)
			{
				mPauseDelayed = true;
				SoundManager::getSingletonPtr()->queueThreadedSound(this, ONLOAD_PAUSE);
			}
			return;
		}

		if (mSource == AL_NONE)
			return;

		alSourcePause(mSource);
		mPauseDelayed = false;
	}

	void StaticSound::play()
	{
		boost::recursive_mutex::scoped_lock l(mMutex);

		if (!mFileOpened)
		{
			if (!mPlayDelayed)
			{
				mPlayDelayed = true;
				SoundManager::getSingletonPtr()->queueThreadedSound(this, ONLOAD_PLAY);
			}
			return;
		}

		if (isPlaying())
			return;

		if (mSource == AL_NONE)
		{
			if (!SoundManager::getSingleton().requestSoundSource(this))
				return;
		}

		if (mPlayPosChanged)
			setPlayPosition(mPlayPos);

		alSourcePlay(mSource);
		mPlay = true;
		mPlayDelayed = false;
	}

	void StaticSound::stop()
	{
		boost::recursive_mutex::scoped_lock l(mMutex);

		if (!mFileOpened)
		{
			if (!mPlayDelayed)
			{
				mPlayDelayed = true;
				SoundManager::getSingletonPtr()->queueThreadedSound(this, ONLOAD_PLAY);
			}
			return;
		}

		if (mSource == AL_NONE)
			return;

		alSourceStop(mSource);
		alSourceRewind(mSource);
		mPlay = false;
		mCurrentPlayPosition = 0;
		mStopDelayed = false;
		if (mGiveUpSource)
			SoundManager::getSingleton().releaseSoundSource(this);
	}

	void StaticSound::loop(bool loop)
	{
		SoundClip::loop(loop);
		if (mSource != AL_NONE)
		{
			alSourcei(mSource, AL_LOOPING, loop);
		}
	}

	void StaticSound::_updateAudioBuffers()
	{
		// Automatically play if ready.
		if (mPlayDelayed)
			play();

		if(mSource == AL_NONE || !mPlay)
			return;

		ALenum state;
		alGetSourcei(mSource, AL_SOURCE_STATE, &state);

		if (state == AL_STOPPED)
		{
			stop();
			// Finished callback
			if (mFinishedCB && mFinCBEnabled)
				mFinishedCB->execute(static_cast<SoundClip*>(this));
		}
		else
		{
			ALint bytes = 0;

			// Use byte offset to work out current position
			alGetSourcei(mSource, AL_BYTE_OFFSET, &bytes);

			// Has the audio looped?
			if (mCurrentPlayPosition > bytes)
			{
				if (mLoopCB && mLoopCBEnabled)
					mLoopCB->execute(static_cast<SoundClip*>(this));
			}

			// Store current offset position
			mCurrentPlayPosition = bytes;
		}
	}
}